package reporting

import (
	"fmt"
	"gitee.com/mootok/mootok/src/constant/strings"
	"gitee.com/mootok/mootok/src/models"
	"gitee.com/mootok/mootok/src/storage/clickhouse"
	"gitee.com/mootok/mootok/src/storage/redis"
	"gitee.com/mootok/mootok/src/utils/logging"
	"gitee.com/mootok/mootok/src/web/api"
	"gitee.com/mootok/mootok/src/web/common/response"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis_rate/v10"
	"github.com/sirupsen/logrus"
	"net/http"
)

var videoShareMaxQPM = 10
var videoShareClickMaxQPM = 10

func genReportVideoShareLimitKey(userID int) string {
	return fmt.Sprintf("%s-%d", reportVideoShareLimitKeyPrefix, userID)
}

func genReportVideoShareClickLimitKey(userID int) string {
	return fmt.Sprintf("%s-%d", reportVideoShareClickLimitKeyPrefix, userID)
}

func (*ApiReportingImpl) ReportVideoShare(c *gin.Context) {
	var err error
	var req api.ReportVideoShareReq
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	err = c.BindJSON(&req)
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}

	logger := logging.LogService("Reporting.VideoShare").WithFields(logrus.Fields{
		"user_id":  userId,
		"video_id": req.VideoId,
	})

	// Rate limit
	limiter := redis_rate.NewLimiter(redis.Client)
	limiterKey := genReportVideoShareLimitKey(userId)
	limiterRes, err := limiter.Allow(c, limiterKey, redis_rate.PerSecond(videoShareMaxQPM))
	if err != nil {
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		logger.Errorf("limiter.Allow: %s", err.Error())
		return
	}
	if limiterRes.Allowed == 0 {
		logger.Errorf("ratelimit hit.")
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RateLimitHitErrorCode, strings.RateLimitHit)
		return
	}

	published, err := VideoService.IsPublished(req.VideoId)
	if err != nil {
		logger.Errorf("failed query video existence: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}
	if !published {
		logger.Infof("video record not found: %v", err)
		response.FailWithMsg(c, http.StatusNotFound, strings.NotFoundErrorCode, strings.NotFoundError)
		return
	}
	if err = clickhouse.Client.Create(&models.ReportedVideoShare{
		VideoId: int32(req.VideoId),
		UserId:  int32(userId),
	}).Error; err != nil {
		logger.Errorf("failed save share record to db: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
		return
	}
	response.Success(c, strings.SuccessCode)
	logger.Info("share saved.")

}

func (*ApiReportingImpl) ReportVideoShareClick(c *gin.Context) {
	var err error
	var req api.ReportVideoShareClickReq
	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	err = c.BindJSON(&req)
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}

	logger := logging.LogService("Reporting.VideoShareClick").WithFields(logrus.Fields{
		"user_id":   userId,
		"video_id":  req.VideoId,
		"sharer_id": req.SharerId,
	})

	// Rate limit
	limiter := redis_rate.NewLimiter(redis.Client)
	limiterKey := genReportVideoShareClickLimitKey(userId)
	limiterRes, err := limiter.Allow(c, limiterKey, redis_rate.PerSecond(videoShareClickMaxQPM))
	if err != nil {
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		logger.Errorf("limiter.Allow: %s", err.Error())
		return
	}
	if limiterRes.Allowed == 0 {
		logger.Errorf("ratelimit hit.")
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RateLimitHitErrorCode, strings.RateLimitHit)
		return
	}

	published, err := VideoService.IsPublished(req.VideoId)
	if err != nil {
		logger.Errorf("failed query video existence: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}
	if !published {
		logger.Infof("video record not found: %v", err)
		response.FailWithMsg(c, http.StatusNotFound, strings.NotFoundErrorCode, strings.NotFoundError)
		return
	}

	//判断分享用户是否存在
	exists, err := UserService.IsExists(req.SharerId)
	if err != nil {
		logger.Errorf("failed query user existence: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
		return
	}

	if !exists {
		logger.Infof("user record not found: %v", err)
		response.FailWithMsg(c, http.StatusNotFound, strings.NotFoundErrorCode, strings.NotFoundError)
		return
	}

	if err = clickhouse.Client.Create(&models.ReportedVideoShareClick{
		VideoId:  int32(req.VideoId),
		UserId:   int32(userId),
		SharerId: int32(req.SharerId),
	}).Error; err != nil {
		logger.Errorf("failed save share click record to db: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
		return
	}
	response.Success(c, strings.SuccessCode)
	logger.Info("share click saved.")
}
